#include "game.h"
#include "stdlib.h"
#include "sdl_helper.h"
#include "game_player.h"
#include "game_map.h"
#include "game_item.h"

/* game world
 * contains a number of characters that can run around
 */
struct world_game {
	struct world w;

	// players (0: user 1: AI)
	struct game_player p[2];

	// map
	struct game_map map;

	// item manager
	struct game_item_manager item_manager;

	enum { GAME_STATE_PLAYING, GAME_STATE_OVER } state;
};

void world_game_init(struct world_game *world) {

	/* Initialize game data */
	game_map_init(&world->map);
	for (int i = 0; i < 2; i++) {
		world->p[i].players = world->p;
		game_player_init(&world->p[i], &world->map);
	}
	world->p[1].rect.x += 100;

	game_item_manager_init(&world->item_manager);

	world->item_manager.map = &world->map;

	world->state = GAME_STATE_PLAYING;

} // init

// update objects
void world_game_update(struct world_game *world) {

	/* when game over triggers, ideally there's an animation
	 * running for a few frames, then move away
	 * for now, just move to main menu
	 */
	if (world->state == GAME_STATE_OVER) {
		// free previous world, move to the next one
		free(c_world);
		c_world = world_menu_create();
		return;
	}

	game_player_ai(&world->p[1]);

	game_map_update(&world->map);
	game_item_manager_update(&world->item_manager);

	// Update players, and keep track how many are alive
	int living_players = 2;
	for (int i = 0; i < 2; i++) {
		if (game_player_get_state(&world->p[i]) != DROWN) {
			game_player_update(&world->p[i]);
		}
		else {
			living_players--;
		}
	}

	// 1 or less players means its game over
	if (living_players <= 1) {
		world->state = GAME_STATE_OVER;
	}

	/* calculage offset to center the active player on the screen
	 * then offset everything affected
	 */
	int offset_x = (GAME_WIDTH /2 -world->p[0].rect.x) *0.1;
	int offset_y = (GAME_HEIGHT/2 -world->p[0].rect.y) *0.1;

	game_item_manager_scroll(&world->item_manager,
			-offset_x,
			-offset_y);

	for (int i = 0; i < 2; i++) {
		game_player_offset(&world->p[i], offset_x, offset_y);
	}
	game_map_offset(&world->map, offset_x, offset_y);

	// each player has a chance to pick up items
	for (int i = 0; i < 2; i++) {
		game_item_manager_collide(&world->item_manager, &world->p[i]);
	}
}

// draw objects
void world_game_draw(struct world_game *world) {
	game_map_draw(&world->map);
	for (int i = 0; i < 2; i++) {
		game_player_draw(&world->p[i]);
	}
	game_map_draw_front(&world->map);
	game_item_manager_draw(&world->item_manager);
}

void world_game_key(struct world_game *world, SDL_Scancode key, int state) {
	if (world->state != GAME_STATE_PLAYING) return;
	switch (key) {
		case SDL_SCANCODE_D:
			if (state == KEY_PRESSED) {
				game_player_moveright(&world->p[0]);
			}
			else {
				game_player_movestop(&world->p[0]);
			}
			break;
		case SDL_SCANCODE_A:
			if (state == KEY_PRESSED) {
				game_player_moveleft(&world->p[0]);
			}
			else {
				game_player_movestop(&world->p[0]);
			}
			break;

		case SDL_SCANCODE_W:
			if (state == KEY_PRESSED) {
				game_player_jump(&world->p[0]);
			}
			break;

		case SDL_SCANCODE_S:
			if (state == KEY_PRESSED) {
				game_player_useskill(&world->p[0]);
			}
			break;

		case SDL_SCANCODE_SPACE:
			if (state == KEY_PRESSED) {
				game_player_attack(&world->p[0]);
			}
			break;

		default:
			break;
	}
}

void world_game_mouse(struct world_game *world,
	int button, int state, int x, int y) {
	(void) world;
	(void) button;
	(void) state;
	(void) x;
	(void) y;
}

/* responsible on initialising a new world of this type
 * and initialising any variable that needs to
 * returns a new instance of this world
 */
struct world *world_game_create() {

	// initialise world
	struct world_game *world = malloc(sizeof(struct world_game));

	// set the world's callbacks to this world's
	world->w.update = (void (*)(void*)) world_game_update;
	world->w.draw   = (void (*)(void*)) world_game_draw;
	world->w.key    = (void (*)(void*, SDL_Scancode, int)) world_game_key;
	world->w.mouse  = (void (*)(void*, int, int, int, int)) world_game_mouse;

	// init
	world_game_init(world);

	// return the instance of this world
	return (struct world*) world;
}
